package pl.edu.mimuw.irs.rcp.controller.editor.report;

import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;

public class ChartBuilder {

	private static DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
	
	private static String X_AXIS_ID = "x";
	private static String Y_AXIS_ID = "y";
	
	private static String CATEGORY_ID_PREFIX = "cat";
	
	// *** Attributes

	private Map<? extends Object, Integer[]> map;
	private DocumentBuilder builder;
	private static Document document;
	private String xAxisLabel;
	private String yAxisLabel;
	private String type;
	private String title;
	
	public Map<? extends Object, Integer[]> getMap() {
		if (map == null) map = new LinkedHashMap<Object, Integer[]>();
		return map;
	}
	public String getxAxisLabel() {
		return xAxisLabel;
	}
	public String getyAxisLabel() {
		return yAxisLabel;
	}
	public String getType() {
		if (type == null) type = Chart.LINE;
		return type;
	}
	public String getTitle() {
		return title;
	}
	
	// *** Constructors
	
	public ChartBuilder(Integer[] values, String type, String title, String xAxisLabel, String yAxisLabel) {
		Map<Object, Integer[]> tmpMap = new LinkedHashMap<Object, Integer[]>();
		tmpMap.put(title, values);
		this.map = tmpMap;
		this.type = type;
		this.title = title;
		this.xAxisLabel = xAxisLabel;
		this.yAxisLabel = yAxisLabel;
	}
	
	public ChartBuilder(Map<? extends Object, Integer[]> map, String type, String title, String xAxisLabel, String yAxisLabel) {
		this.map = map;
		this.type = type;
		this.title = title;
		this.xAxisLabel = xAxisLabel;
		this.yAxisLabel = yAxisLabel;
	}
	
	// *** Methods
	
	public Document createChartDOM() {
		try {
			builder = factory.newDocumentBuilder();
		} catch (ParserConfigurationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		document = builder.newDocument();
		document.setXmlStandalone(true);
		
		/* <chart width="300" height="250" minWidth="0" minHeight="0"> */
		document.appendChild(createChart());
		
		return document;
	}

	private Element createChart() {
		/* <chart width="300" height="250" minWidth="0" minHeight="0"> */
		
		Element chart = document.createElement("chart");
		chart.setNodeValue("chart");
		chart.setAttribute("width", "320");
		chart.setAttribute("height", "220");
		chart.setAttribute("minWidth", "320");
		chart.setAttribute("minHeight", "220");
//		chart.setAttribute("width", "400");
//		chart.setAttribute("height", "200");
//		chart.setAttribute("minWidth", "400");
//		chart.setAttribute("minHeight", "200");
		
		chart.appendChild(createLegend());
		chart.appendChild(createChartTitle());
		chart.appendChild(createPlotArea());
		
		return chart;
	}

	private Node createLegend() {
		/* <legend show="true" alignment="top"/> */
		
		Element legend = document.createElement("legend");
		legend.setAttribute("show", "true");
		legend.setAttribute("alignment", "top");
		
		return legend;
	}

	private Node createChartTitle() {
		/* <chartTitle label="Line Chart"/> */
		
		Element chartTitle = document.createElement("chartTitle");
		chartTitle.setAttribute("label", getTitle());
		
		return chartTitle;
	}

	private Node createPlotArea() {
		/* <plotArea type="line"> */
		
		Element plotArea = document.createElement("plotArea");
		plotArea.setAttribute("type", getType());
		
		plotArea.appendChild(createAxes());
		plotArea.appendChild(createDatasets());
		
		return plotArea;
	}

	private Node createAxes() {
		/* <axes> */
		
		Element axes = document.createElement("axes");
		axes.appendChild(createAxisX());
		axes.appendChild(createAxisY());
		
		return axes;
	}

	private Node createAxisX() {
		/* <axis id="x" title="independent axis"> */
		
		Element axis = document.createElement("axis");
		axis.setAttribute("id", X_AXIS_ID);
		axis.setAttribute("title", getxAxisLabel());
		axis.appendChild(createCategories());
		
		return axis;
	}

	private Node createCategories() {
		/* <categories> */
		
		Element categories = document.createElement("categories");

		int size = 0;
		for (Entry<? extends Object, Integer[]> entry : getMap().entrySet()) {
			size = entry.getValue().length;
			break;
		}
		
		for (int i = 0; i < size; i++) {
			categories.appendChild(
					createCategory(CATEGORY_ID_PREFIX + i, ""));
		}
		
		return categories;
	}

	private Node createCategory(String id, String label) {
		/* <category id="c1" label="A"/> */
		
		Element category = document.createElement("category");
		category.setAttribute("id", id);
		category.setAttribute("label", label);
		
		return category;
	}

	private Node createAxisY() {
		/* <axis id="y" title="primary dependent axis"> */
		
		Element axis = document.createElement("axis");
		axis.setAttribute("id", Y_AXIS_ID);
		axis.setAttribute("title", getyAxisLabel());
		
		return axis;
	}

	private Node createDatasets() {
		/* <datasets> */
		
		Element datasets = document.createElement("datasets");
		for (Entry<? extends Object, Integer[]> entry : getMap().entrySet()) {
			datasets.appendChild(createDataset(entry.getKey().toString(), entry.getValue()));
		}
		
		return datasets;
	}

	private Node createDataset(String label, Integer[] values) {
		/* <dataset label="Dataset 1"> */
		
		Element dataset = document.createElement("dataset");
		dataset.setAttribute("label", label);

		for (int i = 0; i < values.length; i++) {
			dataset.appendChild(createPoint(CATEGORY_ID_PREFIX + i, values[i]));
		}
//		for (int i = values.length - 1; i >= 0; i--) {
//			dataset.appendChild(createPoint(CATEGORY_ID_PREFIX + i, values[i]));
//		}
		
		return dataset;
	}

	private Node createPoint(String x, int y) {
		/* <point> */
		
		Element point = document.createElement("point");
		point.appendChild(createCoordX(x));
		point.appendChild(createCoordY(y));
		
		return point;
	}

	private Node createCoordX(String x) {
		/* <coord axis="x" value="c1"/>*/
		
		Element coord = document.createElement("coord");
		coord.setAttribute("axis", X_AXIS_ID);
		coord.setAttribute("value", x);
		
		return coord;
	}

	private Node createCoordY(int y) {
		/* <coord axis="y" value="0.1"/> */

		Element coord = document.createElement("coord");
		coord.setAttribute("axis", Y_AXIS_ID);
		coord.setAttribute("value", String.valueOf(y));
		
		return coord;
	}
}
